package oxml

import (
	"os"
	"strings"
)

//-----------------------------------------------------------------------------
//S XML 파싱 모듈

	//S Namespace
	type Namespace struct {
		names  string
    	nsUri string
	}
	func NewNamespace() Namespace {
		return Namespace{"",""}
	}
	func (v *Namespace) NewNamespace(name, uri string) *Namespace {
		return &Namespace{name, uri}
	}
	func (v *Namespace) GetNamespaceUri() string {
		return v.nsUri
	}
	func (v *Namespace) GetNamespace() string {
		return v.names
	}
	//e Namespace
	
	//S Attribute
	type Attribute struct {
		ns *Namespace
		attrName  string
    	value string
	}
	func NewAttribute(name, value string) *Attribute {
		return &Attribute{nil,name,value}
	}
	func (v *Attribute) GetName() string {
		return v.attrName
	}
	func (v *Attribute) GetValue() string {
		return v.value
	}
	func (v *Attribute) SetValue(txt string) *Attribute {
		strs := v.split(txt, "&")
		cnt := len(strs)
		if cnt == 0 {
			v.value = ""
			return v
		}
//		fmt.Println("lksdjflkdsjf->>>",strs)
		v.value = strings.Trim(strs[0]," ")
		for i:=0; i < cnt; i++ {
			v.value += "&amp;" + strings.Trim(strs[i]," ")
		}
//		v.value = txt
		return v
	}
	func (v *Attribute) split(original, separator string) []string {
		nodes := make([]string, 0)
		index := strings.Index(original, separator)
		for index >= 0 {
			nodes = append(nodes, original[0:index])
			original = original[index+len(separator):]
			index = strings.Index(original, separator)
		}
		return nodes
	}
	//E Attribute
	
	//S AttrList
	type AttrList struct {
		alist []*Attribute
	}
	func NewAttrList() *AttrList {
		a := &AttrList{make([]*Attribute, 0)}
		return a
	}
	func (v *AttrList) Size() int {
		return len(v.alist)
	}
	func (v *AttrList) AddElement(attr *Attribute) {
		v.alist = append(v.alist, attr)
	}
	func (v *AttrList) Get(idx int) *Attribute {
		if idx < 0 && idx >= v.Size() {
			return nil
		}
		return v.alist[idx]
	}
	func (v *AttrList) Remove(idx int) {
		if idx < 0 && idx >= v.Size() {
			return
		}
		v.alist = append(v.alist[:idx], v.alist[idx+1:]...)
	}
	func (v *AttrList) RemoveAll() {
		v.alist = make([]*Attribute, 0)
	}
	//E AttrList
	
	//S AttrList
	type ElemList struct {
		elist []*Element
	}
	func NewElemList() *ElemList {
		e := &ElemList{make([]*Element, 0)}
		return e
	}
	func (v *ElemList) Size() int {
		return len(v.elist)
	}
	func (v *ElemList) AddElement(attr *Element) {
		v.elist = append(v.elist, attr)
	}
	func (v *ElemList) Get(idx int) *Element {
		if idx < 0 && idx >= v.Size() {
			return nil
		}
		return v.elist[idx]
	}
	func (v *ElemList) RemoveElement(el *Element) {
		for i:=0;i<v.Size();i++ {
			if v.elist[i] == el {
				v.elist = append(v.elist[:i], v.elist[i+1:]...)
				break
			}
		}
	}
	func (v *ElemList) Remove(idx int) {
		if idx < 0 && idx >= v.Size() {
			return
		}
		v.elist = append(v.elist[:idx], v.elist[idx+1:]...)
	}
	func (v *ElemList) RemoveAll() {
		v.elist = make([]*Element, 0)
	}
		
	//S Element
//	type Element struct {
//		elemName string
//		value	string
//		attrs	*AttrList
//		ns	 Namespace
//		eles	*ElemList
//		parentElement *Element
//	}
//	func NewElement(name string) *Element {
//        return &Element{name, "", NewAttrList(), NewNamespace(), NewElemList(), nil}
//    }
//	func (v *Element) init(ename, value string) {
//		v.elemName = ename
//		v.value = value
//	}
//	func (v *Element) GetNamespace() Namespace {
//		return v.ns
//	}
//	func (v *Element) SetNamespace(ns Namespace) {
//		v.ns = ns
//		if ns.GetNamespace() == "" {
//			v.SetAttribute("xmlns", ns.GetNamespaceUri())
//		} else {
//			v.SetAttribute(ns.GetNamespace(), ns.GetNamespaceUri())
//		}
//	}
//	func (v *Element) SetAttribute(name,txt string) *Element {
//		cnt := v.attrs.Size()
//		for i:=0;i<cnt;i++ {
//			attr := v.attrs.Get(i)
//			if attr.GetName() == name {
//				attr.SetValue(txt)
//				return v
//			}
//		}
//		v.attrs.AddElement(&Attribute{nil,name,txt})
//		return v
//	}
//	func (v *Element) SetAttributeNS(name,txt string, ns *Namespace) *Element {
//		cnt := v.attrs.Size()
//		for i:=0;i<cnt;i++ {
//			attr := v.attrs.Get(i)
//			if attr.GetName() == name {
//				attr.SetValue(txt)
//				return v
//			}
//		}
//		v.attrs.AddElement(&Attribute{ns,name,txt})
//		return v
//	}
//	func (v *Element) AddNamespaceDeclaration(ns *Namespace) {
//		v.SetAttribute(ns.GetNamespace(), ns.GetNamespaceUri())
//	}
//	func (v *Element) SetParentElement(el *Element) {
//		v.parentElement = el
//	}
//	func (v *Element) GetParentElement() *Element {
//		return v.parentElement
//	}
//	func (v *Element) RemoveChildIdx(idx int) {
//		v.eles.Remove(idx)
//	}
//	func (v *Element) RemoveChildName(name string) {
//		for i := 0; i < v.eles.Size(); i++ {
//			ele := v.eles.Get(i)
//			if ele.GetName() == name {
//				v.eles.Remove(i)
//				break
//			}
//		}
//	}
//	func (v *Element) RemoveChild(el *Element) {
//		v.eles.RemoveElement(el)
//	}
//	func (v *Element) GetName() string {
//		return v.elemName
//	}
//	func (v *Element) GetText() string {
//		return v.value
//	}
//	func (v *Element) GetChildren() *ElemList {
//		return v.eles
//	}
//	func (v *Element) GetChildrenName(name string) ElemList {
//		var newEles ElemList
//		for i:=0;i<v.eles.Size();i++ {
//			ele := v.eles.Get(i)
//			if ele.GetName() == name {
//				newEles.AddElement(ele)
//			}
//		}
//		return newEles
//	}
//	func (v *Element) GetChild(name string) *Element {
//		for i:=0;i<v.eles.Size();i++ {
//			ele := v.eles.Get(i)
//			if ele.GetName() == name {
//				return ele
//			}
//		}
//		return nil
//	}
//	func (v *Element) GetChildText(name string) string {
//		el := v.GetChild(name)
//		if el == nil {
//			return ""
//		}
//		return v.GetChild(name).GetText()
//	}
//	func (v *Element) AddContent(el *Element) *Element {
//		el.SetParentElement(v)
//		v.eles.AddElement(el)
//		return el
//	}
//	func (v *Element) AddChild(name string) *Element {
//		newel := NewElement(name)
//		newel.SetParentElement(v)
//		v.eles.AddElement(newel)
//		return newel
//	}
//	func (v *Element) SetText(txt string) *Element {
//		v.value = txt
//		return v
//	}
//	func (v *Element) SetAttributes(al *AttrList) *Element {
//		v.attrs = al
//		return v
//	}
//	func (v *Element) GetAttributes() *AttrList {
//		return v.attrs
//	}
//	func (v *Element) GetAttribute(name string) *Attribute {
//		for i:=0;i<v.attrs.Size();i++ {
//			attr := v.attrs.Get(i)
//			if attr.GetName() == name {
//				return attr
//			}
//		}
//		return nil
//	}
//	func (v *Element) GetAttributeValue(name string) string {
//		el := v.GetAttribute(name)
//		if el == nil {
//			return ""
//		}
//		return v.GetAttribute(name).GetValue()
//	}
//	func (v *Element) RemoveAttribute(name string) {
//		for i:=0;i<v.attrs.Size();i++ {
//			attr := v.attrs.Get(i)
//			if attr.GetName() == name {
//				v.attrs.Remove(i)
//			}
//		}
//	}
//	func(v *Element) GetDocument() *Document {
//		return NewDocument(v.GetName())
//	}
//	/*
//	public Document getOwnerDocument() {
//		Element newele = null;
//		while(true) {
//			Element ele = parentElement;
//			if(ele == null) break;
//			newele = ele;
//			ele = ele.getParentElement();
//		}
//		if(newele != null)
//			return new Document().build(newele);
//		return null;
//	}
//
//	public void removeChild(String name) {
//		int cnt = eles.size();
//		for(int i = 0; i<cnt;i++) {
//			Element ne = (Element) eles.elementAt(i);
//			if(ne.getName().equals(name)) {
//				eles.removeElement(ne);
//				return;
//			}
//		}
//	}
//	public void removeChild(String name, Namespace ns) {
//		int cnt = eles.size();
//		if(ns != null && !ns.getNamespace().equals("")) name = ns.getNamespace()+":"+ name;
//		for(int i = 0; i<cnt;i++) {
//			Element ne = (Element) eles.elementAt(i);
//			if(ne.getName().equals(name)) {
//				eles.removeElement(ne);
//				return;
//			}
//		}
//	}
//	public void removeChildren(String name) {
//		int cnt = eles.size();
//		for(int i = cnt-1;i >= 0;i--) {
//			Element ne = (Element) eles.elementAt(i);
//			if(ne.getName().equals(name)) {
//				eles.removeElement(ne);
//			}
//		}
//	}
//	public void removeChildren(String name, Namespace ns) {
//		int cnt = eles.size();
//		if(ns != null && !ns.getNamespace().equals("")) name = ns.getNamespace()+":"+ name;
//		for(int i = cnt-1;i >= 0;i--) {
//			Element ne = (Element) eles.elementAt(i);
//			if(ne.getName().equals(name)) {
//				eles.removeElement(ne);
//			}
//		}
//	}	
//	*/
	//E Element
	
	//S Document
	type Document struct {
		header string
		root *Element
	}
	func NewDocument(rootelemname string) *Document {
		return &Document{"<?xml version=\"1.0\" encoding=\"utf-8\"?>", NewElement(rootelemname)}
	}
	func (v *Document) SetHeader(ver ,encode string) {
		v.header = "<?xml version=\""+ver+"\" encoding=\"" + encode + "\"?>"
	}
	func (v *Document) SetRootElement(ele *Element) {
		v.root = ele
	}
	func (v *Document) GetRootElement() *Element {
		return v.root
	}
	func (v *Document) convertXmlString(ele *Element, level int) string {
		chum := "\n"
		for i := 0; i <level;i++ {
			chum += "\t"
		}
		xmlStr := chum+"<" + ele.GetName()
		strAttr := ""
//		fmt.Println(ele.GetAttributes().Size())
		for i := 0; i<ele.GetAttributes().Size();i++ {
			attr := ele.GetAttributes().Get(i)
			strAttr += " " + attr.GetName()+"=\""+attr.GetValue()+"\""
		}
		xmlStr += strAttr + ">"
		if ele.GetText() != "" {
			xmlStr += ele.GetText()
			xmlStr += "</" + ele.GetName()+">"
		} else {
			cnt := ele.GetChildren().Size()
			for i:=0; i < cnt; i++ {
				xmlStr += v.convertXmlString(ele.GetChildren().Get(i), level+1)
			}
			if cnt == 0 {
				xmlStr += "</" + ele.GetName()+">"
			} else {
				xmlStr += chum + "</" + ele.GetName()+">"
			}
		}
		return xmlStr
	}
	func (v *Document) XmlToString() string {
		xmlStr := v.header
		xmlStr += v.convertXmlString(v.root, 0)
		return xmlStr
	}
	/*
	public byte[] xmlToBytes() {
		return xmlToString().getBytes();
	}
	public byte[] xmlToBytes(String encodding) {
		try {
			return xmlToString().getBytes(encodding);
		} catch (UnsupportedEncodingException e) {
			System.out.println("gXml : Document : Error -> " + e.getMessage());
			return null;
		}
	}
	*/
	//E Document
	
	func ParserFile(fullfilename string) (*Document, error) {
		inFile, inErr := os.Open(fullfilename)
		defer inFile.Close()
		if(inErr != nil) {
			return nil, inErr
		}
    	inBuf := make([]byte, 1000000)
    	n, inErr := inFile.Read(inBuf)
    	if inErr != nil {
//    		fmt.Println("\n", inErr)
    		return nil,inErr;
    	}

		return Parser(string(inBuf[0:n]))
	}
	func Parser(strs string) (*Document, error) {
		doc := &Document{} 
        
        strs = strings.SplitN(strs, "<", 2)[1]
        
        if strings.EqualFold(strs[0:4], "?xml") { //<?xml version="1.0" encoding="UTF-8"?>
			strlen := strings.Index(strs,"?>")
        	doc.header = "<" + strs[0:strlen]+"?>"
        	strs = strs[strlen+2:len(strs)]
        } else {
        	strs = "<" + strs
        }
		tmps := strings.SplitN(strs, "<!--", 2)
		for len(tmps) > 1 {
			tmp2 := strings.SplitN(tmps[1], "-->", 2)[1]
			strs = tmps[0] + tmp2
			tmps = strings.SplitN(strs, "<!--", 2)
		}
       	ele, err := parser(strs)
       	if err < 0 {
       		return nil, nil
       	}
       	doc.root = ele
		return doc, nil
	}
	func parserName(strs string) string {
		startpos := strings.Index(strs, "<")+1
		endpos := strings.Index(strs, ">")
		str := strs[startpos:endpos]
		bl := strings.Index(str, " ")
		name := strings.Trim(str, " ")
		
		if bl > 0 {
			name = str[0:bl]
		}
		return name		
	}
	func parser(strs string) (*Element, int) { //(string, string, *AttrList, string, int) {
		if strs == "" {
			return nil, -1 
		}
		attrs := NewAttrList()
		startpos := strings.Index(strs, "<")+1
		endpos := strings.Index(strs, ">")
		str := strs[startpos:endpos]
//		fmt.Println("\n"+str)
		bl := strings.Index(str, " ")
		name := strings.Trim(str, " ")
		if bl > 0 {
			name = str[0:bl]
			tmpstr := strings.Trim(str[bl:], "/")
			tmpstr = strings.Replace(tmpstr, "\n", " ", -1)
			tmpstr = strings.Replace(tmpstr, "\r", " ", -1)
			astrs := strings.Split(tmpstr," ")
			for i:=0;i<len(astrs);i++ {
				if astrs[i] == "" {
					continue
				}
				aastrs := strings.Split(astrs[i],"=")
				attr := NewAttribute(aastrs[0], strings.Trim(aastrs[1],"\""))
				attrs.AddElement(attr)
			}
		}
		ele := NewElement(strings.Trim(name, "/"))
		ele.SetAttributes(attrs)
//		fmt.Print("\n"+name)

		index := strings.Index(strs[endpos+1:], "</"+name+">")
		if index < 0 {
			return ele, 0
		} else {
			strs = strings.SplitN(strs[endpos+1:], "</"+name+">",2)[0]
		}
		for len(strs) > 0 {
			for i:=0;i<len(strs);i++ {
				if strs[i] == ' ' || strs[i] == '\n' || strs[i] == '\t' || strs[i] == '\r' { //|| strs[i] == ''  || strs[i] == '' 
					continue
				}
				strs = strs[i:]
				break
			}
			if strs[0] == '<' && strs[:2] != "</" {
				tmpname := parserName(strs)
				tmp := ""
				index := strings.Index(strs, "</"+tmpname+">")
				if index < 0 {
					index := strings.Index(strs, "/>")
					if index > -1 {
						tmp = strings.SplitN(strs, "/>",2)[1]
//						fmt.Println("\ndddd--->", tmp)
					} else {
						return nil, -1
					}
				} else {
					tmp = strings.SplitN(strs, "</"+tmpname+">", 2)[1]
				}
				
				subele, _ := parser(strs)
				ele.AddContent(subele)
				strs = tmp
//				fmt.Println("\n"+strs)
					
			} else {
				values := strings.SplitN(strs,"<",2)
				ele.SetText(strings.TrimSpace(values[0]))
				if len(values) > 1 {
					strs = "<"+values[1]
				} else {
					strs = ""
				}
			}
		}
		return ele, 0
	}
//E XML 파싱 모듈

	//S Element
	type Element struct {
		elemName string
		value	string
		attrs	*AttrList
		ns	 Namespace
		eles	*ElemList
		parentElement *Element
	}
	func NewElement(name string) *Element {
        return &Element{name, "", NewAttrList(), NewNamespace(), NewElemList(), nil}
    }
	func (v *Element) init(ename, value string) {
		v.elemName = ename
		v.value = value
	}
	func (v *Element) GetNamespace() Namespace {
		return v.ns
	}
	func (v *Element) SetNamespace(ns Namespace) {
		v.ns = ns
		if ns.GetNamespace() == "" {
			v.SetAttribute("xmlns", ns.GetNamespaceUri())
		} else {
			v.SetAttribute(ns.GetNamespace(), ns.GetNamespaceUri())
		}
	}
	func (v *Element) SetAttribute(name,txt string) *Element {
		cnt := v.attrs.Size()
		for i:=0;i<cnt;i++ {
			attr := v.attrs.Get(i)
			if attr.GetName() == name {
				attr.SetValue(txt)
				return v
			}
		}
		v.attrs.AddElement(&Attribute{nil,name,txt})
		return v
	}
	func (v *Element) SetAttributeNS(name,txt string, ns *Namespace) *Element {
		cnt := v.attrs.Size()
		for i:=0;i<cnt;i++ {
			attr := v.attrs.Get(i)
			if attr.GetName() == name {
				attr.SetValue(txt)
				return v
			}
		}
		v.attrs.AddElement(&Attribute{ns,name,txt})
		return v
	}
	func (v *Element) AddNamespaceDeclaration(ns *Namespace) {
		v.SetAttribute(ns.GetNamespace(), ns.GetNamespaceUri())
	}
	func (v *Element) SetParentElement(el *Element) {
		v.parentElement = el
	}
	func (v *Element) GetParentElement() *Element {
		return v.parentElement
	}
	func (v *Element) RemoveChildIdx(idx int) {
		v.eles.Remove(idx)
	}
	func (v *Element) RemoveChildName(name string) {
		for i := 0; i < v.eles.Size(); i++ {
			ele := v.eles.Get(i)
			if ele.GetName() == name {
				v.eles.Remove(i)
				break
			}
		}
	}
	func (v *Element) RemoveChild(el *Element) {
		v.eles.RemoveElement(el)
	}
	func (v *Element) GetName() string {
		return v.elemName
	}
	func (v *Element) GetText() string {
		return v.value
	}
	func (v *Element) GetChildren() *ElemList {
		return v.eles
	}
	func (v *Element) GetChildrenName(name string) ElemList {
		var newEles ElemList
		for i:=0;i<v.eles.Size();i++ {
			ele := v.eles.Get(i)
			if ele.GetName() == name {
				newEles.AddElement(ele)
			}
		}
		return newEles
	}
	func (v *Element) GetChild(name string) *Element {
		for i:=0;i<v.eles.Size();i++ {
			ele := v.eles.Get(i)
			if ele.GetName() == name {
				return ele
			}
		}
		return nil
	}
	func (v *Element) GetChildText(name string) string {
		el := v.GetChild(name)
		if el == nil {
			return ""
		}
		return v.GetChild(name).GetText()
	}
	func (v *Element) AddContent(el *Element) *Element {
		el.SetParentElement(v)
		v.eles.AddElement(el)
		return el
	}
	func (v *Element) AddChild(name string) *Element {
		newel := NewElement(name)
		newel.SetParentElement(v)
		v.eles.AddElement(newel)
		return newel
	}
	func (v *Element) SetText(txt string) *Element {
		v.value = txt
		return v
	}
	func (v *Element) SetAttributes(al *AttrList) *Element {
		v.attrs = al
		return v
	}
	func (v *Element) GetAttributes() *AttrList {
		return v.attrs
	}
	func (v *Element) GetAttribute(name string) *Attribute {
		for i:=0;i<v.attrs.Size();i++ {
			attr := v.attrs.Get(i)
			if attr.GetName() == name {
				return attr
			}
		}
		return nil
	}
	func (v *Element) GetAttributeValue(name string) string {
		el := v.GetAttribute(name)
		if el == nil {
			return ""
		}
		return v.GetAttribute(name).GetValue()
	}
	func (v *Element) RemoveAttribute(name string) {
		for i:=0;i<v.attrs.Size();i++ {
			attr := v.attrs.Get(i)
			if attr.GetName() == name {
				v.attrs.Remove(i)
			}
		}
	}
	func(v *Element) GetDocument() *Document {
		return NewDocument(v.GetName())
	}
	/*
	public Document getOwnerDocument() {
		Element newele = null;
		while(true) {
			Element ele = parentElement;
			if(ele == null) break;
			newele = ele;
			ele = ele.getParentElement();
		}
		if(newele != null)
			return new Document().build(newele);
		return null;
	}

	public void removeChild(String name) {
		int cnt = eles.size();
		for(int i = 0; i<cnt;i++) {
			Element ne = (Element) eles.elementAt(i);
			if(ne.getName().equals(name)) {
				eles.removeElement(ne);
				return;
			}
		}
	}
	public void removeChild(String name, Namespace ns) {
		int cnt = eles.size();
		if(ns != null && !ns.getNamespace().equals("")) name = ns.getNamespace()+":"+ name;
		for(int i = 0; i<cnt;i++) {
			Element ne = (Element) eles.elementAt(i);
			if(ne.getName().equals(name)) {
				eles.removeElement(ne);
				return;
			}
		}
	}
	public void removeChildren(String name) {
		int cnt = eles.size();
		for(int i = cnt-1;i >= 0;i--) {
			Element ne = (Element) eles.elementAt(i);
			if(ne.getName().equals(name)) {
				eles.removeElement(ne);
			}
		}
	}
	public void removeChildren(String name, Namespace ns) {
		int cnt = eles.size();
		if(ns != null && !ns.getNamespace().equals("")) name = ns.getNamespace()+":"+ name;
		for(int i = cnt-1;i >= 0;i--) {
			Element ne = (Element) eles.elementAt(i);
			if(ne.getName().equals(name)) {
				eles.removeElement(ne);
			}
		}
	}	
	*/
	//E Element
//------------------------------------------------------------------------------